package com.hclc.util.reflection;

import com.esotericsoftware.reflectasm.ConstructorAccess;
import com.esotericsoftware.reflectasm.FieldAccess;
import com.esotericsoftware.reflectasm.MethodAccess;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName ReflectionUtil
 * @Author: yurj
 * @Mail：1638234804@qq.com
 * @Date: Create in 10:54 2020/10/19
 * @version: 1.0
 */
@Slf4j
public class ReflectionUtil {
    /**
     * 属性前缀get
     */
    private static final String ATTRIBUTE_PREFIX_GET = "get";

    /**
     * 属性前缀is
     */
    private static final String ATTRIBUTE_PREFIX_IS = "is";

    /**
     * 方法缓存实例map
     */
    private static Map<String, MethodAccess> METHOD_ACCESS_CACHE = new ConcurrentHashMap<>();

    /**
     * 字段缓存实例map
     */
    private static Map<String, FieldAccess> FIELD_ACCESS_CACHE = new ConcurrentHashMap<>();

    /**
     * 构造方法缓存实例map
     */
    private static Map<String, ConstructorAccess> CONSTRUCTOR_ACCESS_CACHE = new ConcurrentHashMap<>();

    /**
     * 缓存MethodAccess
     *
     * @param obj
     * @return com.esotericsoftware.reflectasm.MethodAccess
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020-4-23 18:17
     */
    public static MethodAccess getMethodAccess(Object obj) {
        MethodAccess access = METHOD_ACCESS_CACHE.get(obj.getClass().toString());
        // 先检查缓存中是否已存在
        if (access == null) {
            try {
                // 生成字节码的方式创建
                access = MethodAccess.get(obj.getClass());
                METHOD_ACCESS_CACHE.put(obj.getClass().toString(), access);
            } catch (Exception e) {
                log.error("获取MethodAccess 异常obj:{}", obj, e);
            }
        }
        return access;
    }

    /**
     * 缓存MethodAccess
     *
     * @param clazz
     * @return com.esotericsoftware.reflectasm.MethodAccess
     * @author yurj
     * @version 3.0
     * @date 2020/12/25 16:09
     */
    public static MethodAccess getMethodAccess(Class clazz) {
        MethodAccess access = METHOD_ACCESS_CACHE.get(clazz.getName());
        // 先检查缓存中是否已存在
        if (access == null) {
            try {
                // 生成字节码的方式创建
                access = MethodAccess.get(clazz);
                METHOD_ACCESS_CACHE.put(clazz.getName(), access);
            } catch (Exception e) {
                log.error("获取MethodAccess 异常obj:{}", clazz, e);
            }
        }
        return access;
    }

    /**
     * 缓存FieldAccess
     *
     * @param obj
     * @return com.esotericsoftware.reflectasm.FieldAccess
     * @author yurj
     * @version 1.0
     * @date 2020/10/19 13:28
     */
    public static FieldAccess getFieldAccess(Object obj) {
        FieldAccess access = FIELD_ACCESS_CACHE.get(obj.getClass().toString());
        // 先检查缓存中是否已存在
        if (access == null) {
            try {
                // 生成字节码的方式创建
                access = FieldAccess.get(obj.getClass());
                FIELD_ACCESS_CACHE.put(obj.getClass().toString(), access);
            } catch (Exception e) {
                log.error("获取FieldAccess 异常obj:{}", obj, e);
            }
        }
        return access;
    }

    /**
     * 缓存ConstructorAccess
     *
     * @param obj
     * @return com.esotericsoftware.reflectasm.ConstructorAccess
     * @author yurj
     * @version 1.0
     * @date 2020/10/19 13:30
     */
    public static ConstructorAccess getConstructorAccess(Object obj) {
        ConstructorAccess access = CONSTRUCTOR_ACCESS_CACHE.get(obj.getClass().toString());
        // 先检查缓存中是否已存在
        if (access == null) {
            try {
                // 生成字节码的方式创建
                access = ConstructorAccess.get(obj.getClass());
                CONSTRUCTOR_ACCESS_CACHE.put(obj.getClass().toString(), access);
            } catch (Exception e) {
                log.error("获取ConstructorAccess 异常obj:{}", obj, e);
            }
        }
        return access;
    }

    /**
     * 获取方法名称
     *
     * @param fieldName
     * @param isUnpackingBoolean
     * @return java.lang.String
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020-4-23 16:36
     */

    private static String formatFieldName(String fieldName, boolean isUnpackingBoolean) throws Exception {
        //是否是拆箱boolean
        if (isUnpackingBoolean) {
            if (fieldName.startsWith("is")) {
                return fieldName;
            } else {
                return ATTRIBUTE_PREFIX_IS + getMethodName(fieldName);
            }
        }
        return ATTRIBUTE_PREFIX_GET + getMethodName(fieldName);

    }

    /**
     * 字符串首字母大写
     *
     * @param fieldName
     * @return java.lang.String
     * @author yurj
     * @mail 1638234804@qq.com
     * @version 1.0
     * @date 2020-4-23 18:30
     */

    private static String getMethodName(String fieldName) {
        Assert.notNull(fieldName, () -> "fieldName is null");
        char c = fieldName.charAt(0);
        if (Character.isUpperCase(c)) {
            return fieldName;
        } else {
            return Character.toUpperCase(c) + fieldName.substring(1);
        }
    }

    /**
     * 用例
     *
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        TestReflection target = new TestReflection();
        // 根据类实例获取对应方法实例
        MethodAccess methodAccess = getMethodAccess(target);
        // 根据方法名获取下标
        int index = methodAccess.getIndex("run", int.class, String.class);
        int index2 = methodAccess.getIndex("run2", int.class, String.class);
        System.out.println(index);
        System.out.println(index2);
        Object invoke = methodAccess.invoke(target, index, 1234, "0101");
        System.out.println(invoke);
        methodAccess.invoke(target, index2, 1234, "0101");
    }
}
